if (!folderplus)
	var folderplus = {};

folderplus.conv = {
	
	
	// ============================================================================================================
	// Find conversation functions                                                                 
	// ============================================================================================================
	
	/**
	 * set move to conversation and go to folder menu in mailContext menu
	 * and go menu.
	 * Async method :-/ First call a globa search to get a gloda message from 
	 * aMsgHdr then call a second gloda search to find all messages from the 
	 * conversation.
	 * @return nothing, use the callback.
	 *    callback param  : a folder uri, "error" => "?".
	 */
	_getFolder : function(aMsgHdr, callback, Gloda) {
		// cf msgHdrViewOverlay.js/ConversationOpener
		/* First, look for a gloda message from messege header. */
		var aListener = {
			onItemsAdded : function myListener_onItemsAdded(aItems, aCollection) {},
			onItemsModified : function myListener_onItemsModified(aItems,aCollection) {},
			onItemsRemoved : function myListener_onItemsRemoved(aItems,aCollection) {},
			onQueryCompleted : function myListener_onQueryCompleted(aCollection) {
				try {
					if (!aCollection.items.length) {
						throw "Couldn't find a glodaMessage for msg: " + aMsgHdr.messageId;
					} else {
						folderplus.log("starting founding conversation for message \"" + aMsgHdr.mime2DecodedSubject + "\"");
						var aGlodaMessage = aCollection.items[0];
						var aConvCollection = aGlodaMessage.conversation
								.getMessagesCollection(aConvListener);
					}
				} catch (e) {
					Components.utils.reportError(e);
					callback("error");
				}
			}
		};
		/* Second, look for a conversation from gloda message. */
		var aConvListener = {
			onItemsAdded : function myListener_onItemsAdded(aItems, aCollection) {},
			onItemsModified : function myListener_onItemsModified(aItems,aCollection) {},
			onItemsRemoved : function myListener_onItemsRemoved(aItems,aCollection) {},
			onQueryCompleted : function myListener_onQueryCompleted(aCollectionConv) {
				try {
					if (!aCollectionConv.items.length) {
						throw "Couldn't find conversation message for msg: " + aMsgHdr.messageId;
					}
					var msgConvCount = aCollectionConv.items.length;
					//var isNewer = folderplusConv._isNewerConv(aCollectionConv.items, aMsgHdr.date);
					var isAllConvInFolder = folderplus.conv._isAllConvInFolder(aCollectionConv.items, aMsgHdr.folder.URI);
					var disableSubjectSearch = false;
					if (!disableSubjectSearch && (msgConvCount == 1 || isAllConvInFolder)) {
						// si le message ne fait parti d'aucune conversation
						// ou qu'il est le 1° d'une conversation qui commence
						// try to found folder by subject
						query=Gloda.newQuery(Gloda.NOUN_MESSAGE);
						var aSubject = aMsgHdr.mime2DecodedSubject;
						//aSubject = folderplusConv._getPrefixeRemoveREFWD(aSubject);
						// no need to remove 're:'.
						aSubject = "\"" + aSubject + "\"";
						/** gloda search on subject is case-insensitive, accent-insensitive, not exact,
						 * seems to automaticaly remove re:, tr:, use operator as '-' so need double quotes.
						 * [Migration SVN] : compte-rendu de réunion => 0 result
						 * \"[Migration SVN] : compte-rendu de réunion\" => 6 results
						 * \"RE: [Migration SVN] : compte-rendu de réunion\" => 6 results
						 */
						folderplus.log("founded by conversation but only message or all founded messages into current folder "+msgConvCount+" => look for subject : " +aSubject);
						query.subjectMatches(aSubject);
						query.getCollection(aSubjectListenerExact);
					} else {
						folderplus.log("founded by conversation, look for rigth folder in conversation");
						var folderURI = folderplus.conv._findConversationFolder(aCollectionConv.items, aMsgHdr);
						callback(folderURI);
					}    					
				} catch (e) {
					Components.utils.reportError(e);
					callback("error");
				}
			}
		};
		/* Third, look for message by subject. */
		var aSubjectListenerExact = {
			onItemsAdded : function myListener_onItemsAdded(aItems, aCollection) {},
			onItemsModified : function myListener_onItemsModified(aItems,aCollection) {},
			onItemsRemoved : function myListener_onItemsRemoved(aItems,aCollection) {},
			onQueryCompleted : function myListener_onQueryCompleted(aCollection) {
				try {
					if (!aCollection.items.length) {
						throw "Couldn't find message by subject";
					}
					var msgConvCount = aCollection.items.length;
					//var isNewer = folderplusConv._isNewerConv(aCollection.items, aMsgHdr.date);
					var isAllConvInFolder = folderplus.conv._isAllConvInFolder(aCollection.items, aMsgHdr.folder.URI)
					if (msgConvCount == 1 || isAllConvInFolder) {
						// try to found folder by prefix
						var aSubject = aMsgHdr.mime2DecodedSubject;
						var re = /(.*)\s*:/g;
						var result = re.exec(aSubject);
						if (result) {
							folderplus.log("only one message by subject founded or all in current folder ("+msgConvCount+") => search by prefix");
							aSubject = "\"" + result[1] + "\"";
							folderplus.log("Prefix used : " + aSubject);
							query=Gloda.newQuery(Gloda.NOUN_MESSAGE);
							query.subjectMatches(aSubject);
							query.getCollection(aPrefixListener);
						} else {
							folderplus.log("only one message found  by subject and can not search by prefix.");
							// null => use current folder.
							callback(null);
						}
					} else {
						folderplus.log("found by subject (more than message : " + aCollection.items.length + "), look for right folder.");						
						var folderURI = folderplus.conv._findConversationFolder(aCollection.items, aMsgHdr);
						callback(folderURI);				
					}
				} catch (e) {
					Components.utils.reportError(e);
					callback("error");
				}
			}
		};
		/* fourth , look for message by prefix. */
		var aPrefixListener = {
			onItemsAdded : function myListener_onItemsAdded(aItems, aCollection) {},
			onItemsModified : function myListener_onItemsModified(aItems,aCollection) {},
			onItemsRemoved : function myListener_onItemsRemoved(aItems,aCollection) {},
			onQueryCompleted : function myListener_onQueryCompleted(aCollection) {
				try {
					if (!aCollection.items.length) {
						throw "Couldn't find message by prefix";
					} else {
						folderplus.log("Found conversation by prefix (" + aCollection.items.length + " messages ), look for the right folder.");
						var folderURI = folderplus.conv._findConversationFolder(aCollection.items, aMsgHdr);
						callback(folderURI);				
					}
				} catch (e) {
					Components.utils.reportError(e);
					callback("error");
				}
			}
		};
		/* First, look for a gloda message from messege header. */
		if (aMsgHdr == null) {
			callback("error");
		} else {
			var aCollection = Gloda.getMessageCollectionForHeader(aMsgHdr, aListener);
		}
	},
	
	/**
	 * test if all messages inside the conv are newer that a specified date.
	 * @returns boolean, true if conv is empty
	 */
	_isNewerConv : function (aConvItems, aMessageDate) {
		for (var i = 0; i < aConvItems.length; i++) {
			var aDate = aConvItems[i].folderMessage.date;
			if (aDate < aMessageDate) {
				return false;
			}
		}
		return true;
	},

	/**
	 * @param String folderURI
	 * @returns boolean, true if conv is empty
	 */
	_isAllConvInFolder : function (aConvItems, aFolderURIComp) {
		for (var i = 0; i < aConvItems.length; i++) {
			if (aConvItems[i].folderMessage) {
				var aFolderURI = aConvItems[i].folderMessage.folder.URI;
				if (aFolderURI != aFolderURIComp) {
					return false;
				}
			} else {
				folderplus.log("can't get folderMessage of gloda message" + aConvItems[i].subject + ", " + aConvItems[i].id);
				// Pas de throw pour tenter de passage au message suivant.
			}
		}
		return true;
	},

	/*
	 * Array[String], empty array if no blackList
	 * "".split("|") = [""] (an array of one empty string).
	 */
	_blackListURIs : new Array(""),
	
	/*
	 * update the black list when a folder changes.
	 */
	_updateBlackListWithURI : function (aOrigFolderURI, aNewFolderURI) {
		var needWrite = false;
		for (var i = 0; i < this._blackListURIs.length; i++) {
		  var reg = new RegExp(aOrigFolderURI + "(?=/|$)");
			var newURI = this._blackListURIs[i].replace(reg, aNewFolderURI);
			this._blackListURIs[i] = newURI;
			needWrite = true;
		}
		return needWrite;
	},
	
	/**
	 * This function decides which Folder use as the "conversation folder".
	 * Look for the last message received in the conversation. Don't use the messages
	 * inside the current folder to avoid to return the 'inbox'.
	 * @params messages collection
	 * @return String folderURI ou null ou "error"
	 */
	_findConversationFolder : function (items, aMessage) {
		var firstDate = null;
		var firstMessage = null;
		/*
		for ( var i = 0; i < items.length; i++) {
			var aItem = items[i];
			var aDate = aItem.folderMessage.date;
			var aFolder = aItem.folderMessage.folder.URI;
			if (firstDate == null || (aDate < firstDate)) {
				firstDate    = aItem.folderMessage.date;
				firstMessage = aItem; 
			}
		}
		*/
		var aMessageDate = aMessage.date;
		var aMessageFolder = aMessage.folder.URI;
		for ( var i = 0; i < items.length; i++) {
			var aItem = items[i];
			if (aItem.folderMessage) {
				//folderplus.log(aItem.subject + ", " + aItem.folderMessage.folder.URI + ", " + new Date(aItem.folderMessage.date/1000).toString());
				var aDate = aItem.folderMessage.date;
				var aFolder = aItem.folderMessage.folder.URI;
				//@ test vérifier la prise en compte de la blacklist dans la détermination de la conversation
				if ((aDate < aMessageDate)
				    && (aFolder != aMessageFolder)
				    && (firstDate == null || (aDate > firstDate)))
				{
					//@ todo déplacer les folder utile au blacklistage au début du tableau pour optimiser les perfs
					//@ todo Optimiser l'algorithme. Tenir compte d'une potentielle récursivité du blacklistage.
					var isBlackListed = this._blackListURIs.indexOf(aFolder) >= 0;
					if (!isBlackListed) {
						firstDate    = aItem.folderMessage.date;
						firstMessage = aItem; 
					} else {
						folderplus.log("blackListed " + aFolder + " found");
					}
				}
			} else {
				folderplus.log("can't get folderMessage of gloda message" + aItem.subject + ", " + aItem.id);
				// Pas de throw pour tenter de passage au message suivant.
			}
		}
		folderplus.log("Conversation found : " + firstMessage.folderURI);
		if (firstMessage) {
			return firstMessage.folderURI;
		} else {
			return null;
		}
	},
	
	_getPrefixeRemoveREFWD : function (aSubject) {
		var regex = /((re|fwd|tr) ?)+: ?/gi;
		var result = aSubject.replace(regex, "");
		return result;
	},
	
	_getPrefixe : function (aSubject) {
		var result = null;
		var aSubjectToPrefixe = folderplus.conv._getPrefixeRemoveREFWD(aSubject);
		var regBracket = /\[(.+)\]/;		
		var resultBracket = regBracket.exec(aSubjectToPrefixe);
		if (resultBracket == null) {
			var regDot = /(.+) ?([:/]| -)/;
			var resultDot = regDot.exec(aSubjectToPrefixe);
			if (resultDot != null) {
				result = resultDot[1].trim();
			}
		} else {
			result = resultBracket[1].trim();
		}
		return result;
	}	
}